import time
import traceback
import re
import urllib
import gviz_api

import recorddt
import appdb
import appcons

from google.appengine.ext.webapp import template

class Formatter(object):
  def respond(self, reqHdlr, data, start):
    return
  
class JSONFormatter(Formatter):
  def respond(self, reqHdlr, data, start):
    reqHdlr.response.headers["Content-Type"] = "text/plain"      
    cols, rows, colOrder = self.fill(reqHdlr, data)
    
    if cols is None:
      reqHdlr.response.out.write("google.visualization.Query.setResponse(\
        {version:'0.5',reqId:'"+str(self.getReqId(reqHdlr))+"',status:'error',errors:\
        [{reason:'invalid_query',message:'Invalid query',\
        detailed_message:'Data not found.'}]});")
      return
      
    data_table = gviz_api.DataTable(cols)
    data_table.AppendData(rows)
    reqHdlr.response.out.write(data_table.ToJSonResponse(
          columns_order = colOrder,
          order_by = colOrder[0],
          req_id = self.getReqId(reqHdlr)))
  
  def getReqId(self, reqHdlr):
    #Get request tqx for response
    tqx = reqHdlr.request.get("tqx")
    reqId = 0
    if tqx:
      params = dict([p.split(":") for p in tqx.split(";")])
      reqId = params["reqId"]
  
  def fill(self, reqHdlr, data):
    return

class RecordJSONFormatter(JSONFormatter):
  def fill(self, reqHdlr, data):
    tags = data[0]
    records = data[1]
                
    datefmt = str(reqHdlr.request.get("dfmt"))
    x = reqHdlr.request.get("x").split(" ")
    y = reqHdlr.request.get("y").split(" ")

    dtStats = self.getDTStats(records, x, y)
    pub, cols, rows, colOrder = dtStats.fill(records, tags, datefmt, x, y)
    data[0].insert(0, pub)
    return cols, rows, colOrder
  
  def getDTStats(self, records, x, y):
    if not len("".join(x)) \
    and not len("".join(y)):
      return recorddt.DTX()
    if "date" in x:
      return recorddt.DTX()
    if "date" in y:
      return recorddt.DTY()
    if not "date" in x \
    and not "date" in y:
      return recorddt.DTTags()
    # More conditions later    
    return recorddt.BaseDT() #default
  
class NextTagJSONFormatter(JSONFormatter):
  def fill(self, reqHdlr, data):
    pagesize = 10
    if reqHdlr.request.get("pagesize").isdigit():
      pagesize = int(reqHdlr.request.get("pagesize"));

    cols = [("name", "string"),("refCount", "number"),("bookmark", "string")]
    rows = [[tag.name, len(tag.records), ""] for tag in data[0]]
    if len(rows) \
    and len(rows) == pagesize:
        rows[-1][2] = "bookmark"
    colOrder = ["name", "refCount", "bookmark"]
    
    return cols, rows, colOrder

class RelatedTagJSONFormatter(JSONFormatter):
  def fill(self, reqHdlr, data):
    pagesize = 10
    if reqHdlr.request.get("pagesize").isdigit():
      pagesize = int(reqHdlr.request.get("pagesize"));

    cols = [("name", "string"),("refCount", "number"),("bookmark", "string")]
    rows = [[tag, 0, ""] for tag in data]
    colOrder = ["name", "refCount", "bookmark"]
    
    return cols, rows, colOrder

  
class HistoryJSONFormatter(JSONFormatter):
  def fill(self, reqHdlr, data):
    cols = [("tags", "string"),("qcount", "number")]
    rows = [["".join(history.tags), history.qcount] for history in data]
    colOrder = ["tags", "qcount"]
    
    return cols, rows, colOrder

class TagIndexJSONFormatter(JSONFormatter):
  def fill(self, reqHdlr, data):
    cols = [("tag", "string")]
    rows = [[t[0]] for t in data[0]]
    colOrder = ["tag"]
    
    return cols, rows, colOrder

class RecordHTMLFormatter(RecordJSONFormatter):
  def respond(self, reqHdlr, data, start):
    cols, rows, colOrder = self.fill(reqHdlr, data)

    pub = data[0][0]
    tags = data[0][1:] + list(pub)    
    data = data[1]
        
    data_table = gviz_api.DataTable(cols)
    data_table.AppendData(rows)
    jsonData = data_table.ToJSon(columns_order = colOrder,
                                 order_by = colOrder[0])
#    htmlData = data_table.ToHtml(columns_order = colOrder,
#                                 order_by = colOrder[0])
    legends, jsonData = self.filterPub(pub, colOrder[1:], jsonData)
    tags = " ".join(tags)
    begin = data[0].date.strftime(recorddt.fmt[data[0].dfmt])
    end = data[-1].date.strftime(recorddt.fmt[data[-1].dfmt])

    values = {"query": reqHdlr.request.get("q"),
              "qc": len(data),
              "charttype": self.getChartType(cols, rows),
              "url": reqHdlr.request.url,
              "tags": tags,
              "begin": begin,
              "end": end,
              "hasdetail": True,
              "fmttags": FormatURLStr(tags),
              "data": jsonData,
#              "table": htmlData,
              "source": set([re.match("(?P<source>http://[^/]*)/.*", rec.source).group("source") for rec in data]),
              "links": set([rec.source for rec in data]),
              "appname": appcons.AppName(),
              "cols": legends,
              "elapse": time.time() - start,
              }
    
    reqHdlr.response.out.write(template.render("./pages/charts.html", values))
  
  def getChartType(self, cols, rows):
    return "LineChart"

  def repl(self, m):
    return re.sub(m.group("tag"), "", m.group())
  
  def filterPub(self, pub, legends, jsonData):
    for tag in pub:
      jsonData = re.sub(u"[' ](?P<tag>%s)[ (]" %(tag), self.repl, jsonData)
      
      tmp = []
      for legend in legends:
        tmp.append(re.sub(u"(^|[ ])(?P<tag>%s)[ (]" %(tag), self.repl, legend).strip())
      legends = tmp
    return legends, jsonData
  
class TagIndexHTMLFormatter(Formatter):
  def respond(self, reqHdlr, data, start):
    if not reqHdlr.request.get("q"):
      reqHdlr.redirect("/querynexttag?" + \
                       reqHdlr.request.get("pagesize"))
      return
    qc = len(data)
    data, navi = self.buildNavi(reqHdlr, reqHdlr.request.get("q"), data)
    tags = []
    query = set(reqHdlr.request.get("q"))
    query.discard(" ")
    for tag in data:
      related, more = self.getRelated(tag[0])
      if 3 == len(tag[1]):
        related = tag[1][2] + list(set(related) - set(tag[1][2]))
      tag2, related = self.formatData(tag[0], related[0:5], query)
      tags.append((tag2, BoldQuery(tag[0], tag[1][1]), related, more))
    
    values = {"query": reqHdlr.request.get("q"),
              "qc": qc,
              "data": tags,
              "navi": navi,
              "appname": appcons.AppName(),
              "elapse": time.time() - start,
              }
    
    #TODO: results.html has several "query*tag" without pagesize parameter.
    reqHdlr.response.out.write(template.render("./pages/results.html", values))
    
  def formatData(self, tag, related, query):
    return FormatURLStr(tag), [(BoldQuery(r, query), FormatURLStr(r)) for r in related]
  
  def getRelated(self, tag):
    max = 5
    related = appdb.GetRelatedTags(tag)[0][0:max]
    return related, max == len(related)
  
  def buildNavi(self, reqHdlr, query, data):
    pagesize = 10
    if reqHdlr.request.get("pagesize").isdigit():
      pagesize = int(reqHdlr.request.get("pagesize"));
    bookmark = 1
    if reqHdlr.request.get("bookmark").isdigit():
      bookmark = int(reqHdlr.request.get("bookmark"));
    maxbk = len(data)/pagesize
    if len(data) % pagesize:
      maxbk += 1
    start = (bookmark-1)*pagesize
    data = data[start:start+pagesize]

    i = 1
    maxPage = bookmark+9
    if maxPage > maxbk:
      maxPage = maxbk
    if bookmark > i:
      navi = self.buildNaviStr(query, pagesize, bookmark-1, appcons.PrevPage())
    else: navi = ""
    while i <= maxPage:
      navi += self.buildNaviStr(query, pagesize, i, str(i), i != bookmark)
      i += 1
    if bookmark < i-1:
      navi += self.buildNaviStr(query, pagesize, bookmark+1, appcons.NextPage())
    return data, navi
  
  def buildNaviStr(self, words,  pagesize, bookmark, display, href=True):
    if href:
      return """<span class="appTag"><a href="/querytagindex?q=""" + \
        FormatURLStr(words) + \
        "&pagesize=" + str(pagesize) + \
        "&bookmark=" + str(bookmark) + \
        "\">" + display + "</a></span>"
    else:
      return """<span class="appTag"><span class="appQuery">""" + display + "</span></span>"

class NextTagHTMLFormatter(TagIndexHTMLFormatter):
  def respond(self, reqHdlr, data, start):
    qc = len(data[0])
    data, navi = self.buildNavi(reqHdlr, "", data)
    tags = []
    for tag in data:
      related, more = self.getRelated(tag)
      tagName, related = self.formatData(tag, related)
      tags.append((tagName, tag, related, more))
    values = {"query": appcons.AllTags(),
              "qc": qc,
              "data": tags,
              "navi": navi,
              "appname": appcons.AppName(),
              "elapse": time.time() - start,
              }

    reqHdlr.response.out.write(template.render("./pages/results.html", values))   
  
  def buildNavi(self, reqHdlr, query, data):
    pagesize = 10
    if reqHdlr.request.get("pagesize").isdigit():
      pagesize = int(reqHdlr.request.get("pagesize"));
    pagenbr = 1
    if reqHdlr.request.get("pagenbr").isdigit():
      pagenbr = int(reqHdlr.request.get("pagenbr"));
    if not reqHdlr.request.get("q"):
      navi = ""
    else:
      # FIXME: if memcache is flushed, data[2](prev) is null, and client will jump to first page.
      navi = """<span class="appTag"><a href="/querynexttag?q=""" + \
          FormatURLStr(data[2]) + \
          "&pagesize=" + str(pagesize) + \
          "&pagenbr=" + str(pagenbr-1) + \
          "\">"+appcons.PrevPage()+"</a></span>"

    navi += "<span class=\"appTag\">" + str((pagenbr-1)*pagesize+1) + "-" + \
      str(pagenbr*pagesize) + "</span>"
    if data[1]:
      navi += """<span class="appTag"><a href="/querynexttag?q=""" + \
            FormatURLStr(data[1]) + \
            "&prevtag=" +  FormatURLStr(reqHdlr.request.get("tag")) + \
            "&pagesize=" + str(pagesize) + \
            "&pagenbr=" + str(pagenbr+1) + \
            "\">"+appcons.NextPage()+"</a></span>"
    return data[0], navi
   
class RelatedTagHTMLFormatter(TagIndexHTMLFormatter):
  def respond(self, reqHdlr, data, start):
    qc = len(data)
    data, navi = self.buildNavi(reqHdlr, reqHdlr.request.get("q"), data)
    tags = []
    for tag in data:
      related, more = self.getRelated(reqHdlr.request.get("q") + " " + tag)
      tagName, related = self.formatData(reqHdlr.request.get("q") + " " + tag, related)
      tags.append((tagName, tag, related, more))
    if not len(tags):
#    or (1 == len(tags) \
#        and 0 == len(tags[reqHdlr.request.get("tag") + " " + tag][1])):
      url = "/queryrecord?q=%s" %(FormatURLStr(reqHdlr.request.get("q")))
      reqHdlr.redirect(url)
      return
    values = {"query": reqHdlr.request.get("q"),
              "qc": qc,
              "data": tags,
              "navi": navi,
              "appname": appcons.AppName(),
              "elapse": time.time() - start,
              }

    reqHdlr.response.out.write(template.render("./pages/results.html", values))
    
  def buildNaviStr(self, words,  pagesize, bookmark, display, href=True):
    if href:
      return """<span class="appTag"><a href="/queryrelatedtag?q=""" + \
        FormatURLStr(words) + \
        "&pagesize=" + str(pagesize) + \
        "&bookmark=" + str(bookmark) + \
        "\">" + display + "</a></span>"
    else:
      return """<span class="appTag"><span class="appQuery">""" + display + "</span></span>"

def replBold(m):
  return "<span class=\"appQuery\">%s</span>" %(m.group("q"))

def BoldQuery(display, query):
  for q in query:
    display = re.sub(u"(?P<q>%s)" %(q), replBold, display)
  return display

#TODO: Register as Django filter
def FormatURLStr(word):
  if isinstance(word, unicode):
    return urllib.quote(word.encode("utf-8"))
  return word

class HTMLErrorFormatter(object):
  def respond(self, reqHdlr, e, start):
    values = {"appname": appcons.AppName(),
              "query": reqHdlr.request.get("q"),
              "fmtquery": FormatURLStr(reqHdlr.request.get("q")),
              "begin": reqHdlr.request.get("begin"),
              "end": reqHdlr.request.get("end"),
              e.__class__.__name__: True,
              "errormsg": re.sub("\n", "<br>", traceback.format_exc()),
              }

    reqHdlr.response.out.write(template.render("./pages/error.html", values))

